/**
 * @author phoenics@126com
 * @date 2017年11月8日 下午3:13:10
 * @version V1.0
 */

package com.jx.gocom.nlp.classify.webservice.restcontroller;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.github.sd4324530.jtuple.Tuple2;
import com.jx.gocom.nlp.classify.webservice.DTO.BaseData;
import com.jx.gocom.nlp.classify.webservice.DTO.EvaluateData;
import com.jx.gocom.nlp.classify.webservice.DTO.EvaluateResult;
import com.jx.gocom.nlp.classify.webservice.DTO.KeywordSingleData;
import com.jx.gocom.nlp.classify.webservice.DTO.KeywordSingleResultData;
import com.jx.gocom.nlp.classify.webservice.DTO.PredictData;
import com.jx.gocom.nlp.classify.webservice.DTO.PredictResult;
import com.jx.gocom.nlp.classify.webservice.DTO.PredictResultList;
import com.jx.gocom.nlp.classify.webservice.restcontroller.base.BaseRestController;
import com.jx.gocom.nlp.classify.webservice.service.ClassifyServiceDim;
import com.jx.gocom.nlp.classify.webservice.service.EvaluateBayes;
import com.jx.gocom.nlp.classify.webservice.service.impl.BayesDim;
import com.jx.gocom.nlp.classify.webservice.service.impl.ClassifyResult;
import com.jx.gocom.nlp.classify.webservice.service.impl.DimData;
import com.jx.gocom.nlp.utils.JacksonUtil;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import phoenics.common.BaseData.StatusData;

/**
 *
 */
@Api(value = "与分类有关的API")
@RestController
@RequestMapping("/nlp/classify")
public class ClassifyRem extends BaseRestController {
	private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ClassifyRem.class);
	@Autowired
	ClassifyServiceDim classifyService;
	@Autowired
	EvaluateBayes evaluateBayes;
	
	@Autowired
    RestTemplate restTemplate;
	@Resource(name="deepService4bayes")
	String deepService4bayes;
	
	private PredictResultList takeMClassify(String dataSource,String position,String tag,String text) {
		if(StringUtils.isBlank(position)) {
			position=BayesDim.NULLPOSITION;
		}
		if(StringUtils.isBlank(tag)) {
			tag=BayesDim.NULLTAG;
		}
		TreeMap<ClassifyResult,String> tt=classifyService.classifies(dataSource, position, tag, text);
		PredictResultList p=new PredictResultList();
		p.setDataSource(dataSource);
		p.setPosition(position);
		p.setTag(tag);
		for(int i=0;i<3;i++) {
			 if(!tt.isEmpty()) {
				 p.getClassify().add( tt.pollFirstEntry().getKey().classification);
			 }
		 }
		 if( p.getClassify().isEmpty()) {
			 p.getClassify().add("");
		 }
		return p;
	}
	@ApiOperation(value = "预测分类", notes = "")  
	@RequestMapping(value = "/predict", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public PredictResult predict(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestBody   PredictData predictData
			) {
		String dataSource=predictData.getDataSource();
		String position=predictData.getPosition();
		String tag=predictData.getTag();
		String text=predictData.getText();
		PredictResultList p=takeMClassify(dataSource, position, tag, text);
		return new PredictResult(predictData.getMsgid(),dataSource, position, tag,p.getClassify().get(0));
	}
	@ApiOperation(value = "预测多个分类", notes = "")  
	@RequestMapping(value = "/predict3", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public PredictResultList predict3(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestBody   PredictData predictData
			) {
		String dataSource=predictData.getDataSource();
		String position=predictData.getPosition();
		String tag=predictData.getTag();
		String text=predictData.getText();
		PredictResultList p=takeMClassify(dataSource, position, tag, text);
		 p.setMsgid(predictData.getMsgid());
		return p;
	}
	private String takeClassKey(EvaluateData e) {
		StringBuffer sb=new StringBuffer();
		sb.append("[");
		sb.append(e.getDataSource());
		sb.append("]");
		sb.append("_");
		sb.append("[");
		sb.append(e.getPosition());
		sb.append("]");
		sb.append("_");
		sb.append("[");
		sb.append(e.getTag());
		sb.append("]");
		sb.append("_");
		sb.append("[");
		sb.append(e.getClassify());
		sb.append("]");
		return sb.toString();
	}
	private String takeClassEvaluate(EvaluateData e) {
		return classifyService.classify(e.getDataSource(),e.getPosition(),e.getTag(),e.getText());
	}

	@ApiOperation(value = "分类评测", notes = "")  
	@RequestMapping(value = "/evaluate", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public EvaluateResult evaluate(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestBody   List<EvaluateData> evaluateData
			) {
		//List<String> reallist, List<String> pridlist
		List<String> pridlist=evaluateData.stream().map(this::takeClassEvaluate).collect(Collectors.toList());
		List<String> reallist=evaluateData.stream().map(this::takeClassKey).collect(Collectors.toList());
		double accuracy=evaluateBayes.evaluate(reallist, pridlist);
		List<String> classifies=reallist.stream().distinct().collect(Collectors.toList());
		EvaluateResult evaluateResult=new EvaluateResult();
		evaluateResult.setAccuracy(accuracy);
		classifies.forEach(classname->{
			logger.info("class=={}",classname);
			EvaluateResult.CalPreRec calPreRec=new EvaluateResult.CalPreRec();
			calPreRec.setClassname(classname);
			Tuple2<Double,Double> tuple2=evaluateBayes.calPreRec(reallist, pridlist, classname);
			calPreRec.setRecall(tuple2.second);
			calPreRec.setPrecision(tuple2.first);
			evaluateResult.getCalPreRecs().add(calPreRec);
		});
		return evaluateResult;
	}
	@ApiOperation(value = "所有分类", notes = "")  
	@RequestMapping(value = "/all", method = RequestMethod.GET)
	@ResponseStatus(HttpStatus.OK)
	public Set<DimData> allClassify(
			@RequestParam(value="token",  required=true) String wptoken
			) {
		return classifyService.allClass();
	}
	@ApiOperation(value = "增加一条数据", notes = "")  
	@RequestMapping(value = "/add", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public StatusData addData(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestParam(value="m", defaultValue="false") boolean wide,
			@RequestBody   BaseData baseData
			) {
		//classifyService.appendData(baseData.getClassify(), baseData.getContent());
		addData(baseData,wide);
		return new StatusData(0,"OK");
	}
	@ApiOperation(value = "增加多条数据", notes = "")  
	@RequestMapping(value = "/adds", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public StatusData addData(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestParam(value="m", defaultValue="false") boolean wide,
			@RequestBody   List<BaseData> baseDatas
			) {
		for(BaseData baseData:baseDatas) {
			addData(baseData,wide);
		}
		return new StatusData(0,"OK");
	}
	@ApiOperation(value = "测试训练", notes = "")  
	@RequestMapping(value = "/trainning", method = RequestMethod.GET)
	@ResponseStatus(HttpStatus.OK)
	public StatusData trainning(
			@RequestParam(value="token",  required=true) String wptoken
			) {
		classifyService.retrainningData();
		return new StatusData(0,"OK");
	}
	@ApiOperation(value = "清除样本和训练", notes = "")  
	@RequestMapping(value = "/cleartrainning", method = RequestMethod.GET)
	@ResponseStatus(HttpStatus.OK)
	public StatusData ctrainning(
			@RequestParam(value="token",  required=true) String wptoken
			) {
		classifyService.deleteAllData();
		return new StatusData(0,"OK");
	}
	@ApiOperation(value = "删除一条数据", notes = "")  
	@RequestMapping(value = "/del", method = RequestMethod.POST)
	@ResponseStatus(HttpStatus.OK)
	public StatusData deleteData(
			@RequestParam(value="token",  required=true) String wptoken,
			@RequestBody   BaseData baseData
			) {
		rData(baseData);
		return new StatusData(0,"OK");
	}
	private void addData(BaseData baseData,boolean wide) {
		if(StringUtils.isBlank(baseData.getDataSource())) {
			return;
		}
		if(StringUtils.isBlank(baseData.getPosition()) && StringUtils.isNotBlank(baseData.getTag())) {
			return;
		}
		if(StringUtils.isBlank(baseData.getPosition()) && StringUtils.isBlank(baseData.getTag())) {
			addData(baseData);
			return;
		}
		if(StringUtils.isNotBlank(baseData.getPosition()) && StringUtils.isBlank(baseData.getTag())) {
			addData(baseData);
			return;
		}
		if(!wide) {
			addData(baseData);
			return;
		}
		// Position & Tag ,Both are not null.
		//Add myself
		addData(baseData);
		BaseData t1=new BaseData();
		BeanUtils.copyProperties(baseData,t1);
		t1.setPosition(null);
		t1.setTag(null);
		//Add  Position & Tag is null
		addData(t1);
		BaseData t2=new BaseData();
		BeanUtils.copyProperties(baseData,t2);
		t2.setTag(null);
		//Add  Position is not null & Tag is null
		addData(t2);
	}
	private void addData(BaseData baseData) {
	
		if(StringUtils.isBlank(baseData.getPosition())) {
			baseData.setPosition(BayesDim.NULLPOSITION);
		}
		if(StringUtils.isBlank(baseData.getTag())) {
			baseData.setTag(BayesDim.NULLTAG);
		}
		 MessageDigest md;
		try {
			 md = MessageDigest.getInstance("MD5");
			 md.update(baseData.toString().getBytes());
			classifyService.appendData( baseData.getDataSource(),baseData.getPosition(),baseData.getTag(),baseData.getClassify(), new BigInteger(1, md.digest()).toString(16), baseData.getContent());
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void rData(BaseData baseData) {
		 MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
			 md.update(baseData.toString().getBytes());
			classifyService.backData(new BigInteger(1, md.digest()).toString(16));
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	private KeywordSingleResultData takeKeyWords(KeywordSingleData keywordSingleData) {
		String jsonStr=restTemplate.postForObject(deepService4bayes,keywordSingleData,String.class );
		return JacksonUtil.readValue(jsonStr, KeywordSingleResultData.class);
	}
}
